package gameobjects;

import interfaces.Collidable;
import interfaces.Movable;
import interfaces.Visible;
import java.awt.Graphics;
import java.awt.Point;
import java.util.LinkedList;
import java.util.List;
import resources.Sound;
import resources.Sprite;
import tools.Bounds;
import tools.CollisionGrid;
import abstracts.Level;
import abstracts.Monster;

/** */
public class Eyeball extends Monster implements Visible, Movable, Collidable {
    /**
     * A virtual cookie for whoever can tell me what this piece of
     * code actually does and why it's better than the obvious way.
     */
    private static final List<Point> VELS = new LinkedList<Point>() {
        private static final long serialVersionUID = -3448181094246345964L;
        {
            add(new Point(0, 0));
            add(new Point(1, 0));
            add(new Point(0, -1));
            add(new Point(-1, 0));
            add(new Point(0, 1));
        }
    };
    /** */
    private static final double FRAME_SPEED = 0.125;

    /** */
    private final Level level;
    /** */
    private final CollisionGrid cg;
    /** */
    private final Bounds bounds;
    /** */
    private final Point vel;

    /** */
    private Sprite sprite;
    /** */
    private double frameIndex;

    /**
     * @param newLevel - the level which contains this object.
     * @param row - the row to create this object in.
     * @param col - the column to create this object in.
     */
    Eyeball(final Level newLevel, final int row, final int col) {
        level = newLevel;
        cg = level.getCollisionGrid();
        bounds = new Bounds(row, col, 24, 24);
        vel = new Point(0, 0);

        frameIndex = 0;
        sprite = Sprite.EYEBALL_LEFT;
    }

    @Override
    public final void move() {
        // Check if we're aligned with the grid
        if (bounds.getCenterX() % Level.GRIDSIZE == Level.GRIDSIZE / 2
                && bounds.getCenterY() % Level.GRIDSIZE == Level.GRIDSIZE / 2) {
            // Change direction
            List<Point> vels = new LinkedList<Point>();
            for (Point v : VELS) {
                // Only pick from free directions
                if (!cg.isSolid(bounds.getMinRow() + v.y,
                        bounds.getMinCol() + v.x)) {
                    vels.add(v);
                }
            }
            if (vels.size() > 1) {
                // Don't stop unless there is no other choice
                vels.remove(new Point(0, 0));
            }
            if (vels.size() > 1) {
                // Don't turn 180 degrees unless there is no other
                // choice
                vels.remove(new Point(-vel.x, -vel.y));
            }

            // Pick a random possible direction
            int i = (int) Math.floor(Math.random() * vels.size());
            vel.x = vels.get(i).x;
            vel.y = vels.get(i).y;

            // Adjust the sprite accordingly
            if (vel.x == -1) {
                sprite = Sprite.EYEBALL_LEFT;
            } else if (vel.x == 1) {
                sprite = Sprite.EYEBALL_RIGHT;
            } else if (vel.y == -1) {
                sprite = Sprite.EYEBALL_UP;
            } else if (vel.y == 1) {
                sprite = Sprite.EYEBALL_DOWN;
            }
        }

        // Move
        cg.removeCollidable(this);
        bounds.x += vel.x;
        bounds.y += vel.y;
        cg.addCollidable(this);
    }

    @Override
    public final void draw(final Graphics g) {
        sprite.drawAnchored(g, (int) frameIndex, bounds);

        frameIndex += FRAME_SPEED;
        while (frameIndex >= sprite.getFrameCount()) {
            frameIndex -= sprite.getFrameCount();
        }
    }

    @Override
    public final double getDepth() {
        return bounds.getMaxY();
    }

    @Override
    public final Bounds getBounds() {
        return bounds;
    }

    @Override
    public final void hurt() {
        Sound.EYEBALL_DEATH.play();
        level.remove(this);
        TaskQueue tasks = new TaskQueue();
        tasks.add(new ShrinkAnimation(bounds, sprite, 0.0625));
        level.add(tasks);
        tasks.run(level);
    }
}
